//
//  XPPXcodeKeyPressed.m
//  XPPXcodePlugin
//
//  Created by yoyokko on 10-4-21.
//  Copyright 2010 yoyokko@gmail.com. All rights reserved.
//

#import "XPPXcodeKeyPressed.h"
#import "XCSourceCodeTextView.h"
#import "XCSourceCodeTextView-PBXAdditions.h"
#import "XCSourceCodeTextView-PrivateSearchingAdditions.h"
#import "XPPXcodePlugin.h"
#import "GTMObjC2Runtime.h"
#import "XPPDefines.h"
#import "XPPXcodeRegisterMenuItem.h"
#import <Carbon/Carbon.h>
#import "XPPAutoCompleteHelper.h"
#import "NSStringAddition.h"



@interface XPPXcodeKeyPressed (Private)

- (void) xpp_selectAll:(id) sender;
- (void) xpp_keyDown:(NSEvent *) event;

@end


@implementation XPPXcodeKeyPressed

// Register our class to perform the swizzle once the plugin has finished
// loading.
+ (void)load {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[XPPXcodePlugin registerSwizzleClass:self];
	[pool drain];
}

// Default initializer. Swizzles [XCSourceCodeTextView keyDown:]
// with our [xpp_keyDown:].
- (id)init {
	if ((self = [super init])) {
		swizzleMethods([self class], [XCSourceCodeTextView class], @selector(xpp_keyDown:), @selector(keyDown:));
		swizzleMethods([self class], [XCSourceCodeTextView class], @selector(xpp_selectAll:), @selector(selectAll:));
	}
	return self;
}

+ (void) xpp_keyDown:(NSEvent *) event withObject:(id) aObject
{
	XCSourceCodeTextView *sourceCodeTextView = (XCSourceCodeTextView *) aObject;
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	
	UInt32 deadKeyState = 0;
	UniCharCount actualCount = 0;
	UniChar baseChar;
	TISInputSourceRef sourceRef = TISCopyCurrentKeyboardLayoutInputSource();
	CFDataRef keyLayoutPtr = (CFDataRef)TISGetInputSourceProperty( sourceRef, kTISPropertyUnicodeKeyLayoutData); 
	CFRelease( sourceRef);
	UCKeyTranslate( (UCKeyboardLayout*)CFDataGetBytePtr(keyLayoutPtr),
				   [event keyCode],
				   kUCKeyActionDown,
				   0,
				   LMGetKbdLast(),
				   kUCKeyTranslateNoDeadKeysBit,
				   &deadKeyState,
				   1,
				   &actualCount,
				   &baseChar);
	
	unsigned int flag = [event modifierFlags];
	//BOOL isCapsPressed = flag & NSAlphaShiftKeyMask ? YES : NO;
	BOOL isShiftPressed = flag & NSShiftKeyMask ? YES : NO;
	//BOOL isControlPressed = flag & NSControlKeyMask ? YES : NO;
	//BOOL isAltPressed = flag & NSAlternateKeyMask ? YES : NO;
	//BOOL isCommandPressed = flag & NSCommandKeyMask ? YES : NO;
	//BOOL isNumPressed = flag & NSNumericPadKeyMask ? YES : NO;
	//BOOL isHelpPressed = flag & NSHelpKeyMask ? YES : NO;
	//BOOL isFunPressed = flag & NSFunctionKeyMask ? YES : NO;

	NSString *realString = (NSString *)[sourceCodeTextView realString];
	switch (baseChar) 
	{
		case 9: // tab
		{
			if (![XPPXcodeRegisterMenuItem isRegistered] || ![defaults boolForKey:XPPXCodeEnableUseDefinedKey])
			{
				[sourceCodeTextView xpp_keyDown:event];
				break;
			}
			else
			{
				NSInteger tag = [defaults integerForKey:XPPXcodeXppDefaultKey];
				
				// xpp default
				if (tag == 0) 
				{
					[[sourceCodeTextView codeAssistant] dismissAssistant];
					
					NSRange autoCompleteRange = [sourceCodeTextView rangeForUserCompletion];
					NSString *prefixString = [realString substringWithRange:autoCompleteRange];
					NSString *insertedText = [[XPPAutoCompleteHelper sharedXPPAutoCompleteHelper] textForPrefix:prefixString];
					if (prefixString && insertedText) 
					{
						int insertIndex = [[XPPAutoCompleteHelper sharedXPPAutoCompleteHelper] cousorIndexForPrefix:prefixString];
						[sourceCodeTextView setSelectedRange:autoCompleteRange];
						[sourceCodeTextView insertText:insertedText];
						// we need to adjust cousor's index
						if(insertIndex > -1)
						{
							NSRange selectRange = [sourceCodeTextView selectedRealRange];
							[sourceCodeTextView setSelectedRange:NSMakeRange(selectRange.location - [insertedText length] + insertIndex,
																			 0)];
						}
					}
					else 
					{
						[sourceCodeTextView xpp_keyDown:event];
					}
				}
				else 
				{
					[sourceCodeTextView xpp_keyDown:event];
				}
			}
		}
			break;
		case 91: //   [ 
		{
			if(!isShiftPressed)
			{
				NSRange selectRange = [sourceCodeTextView selectedRealRange];
				NSString *newString;
				if(selectRange.length == 0)
				{
					newString = [[NSString alloc] initWithString:@"[]"];
				}else
				{
					NSString *selectedString = [realString substringWithRange:selectRange];
					newString = [[NSString alloc] initWithFormat:@"[%@]", selectedString];
				}
				[sourceCodeTextView insertText:newString];
				selectRange = [sourceCodeTextView selectedRealRange];
				[sourceCodeTextView setSelectedRange:NSMakeRange(selectRange.location - 1, 0)];
				[newString release];
			}else
			{
				[sourceCodeTextView xpp_keyDown:event];
			}
		}
			break;
			
		case 57: // (
		{
			if(isShiftPressed && [XPPXcodeRegisterMenuItem isRegistered])
			{
				NSRange selectRange = [sourceCodeTextView selectedRealRange];
				NSString *newString;
				if(selectRange.length == 0)
				{ 
					newString = [[NSString alloc] initWithString:@"()"];
				}else
				{
					NSString *selectedString = [realString substringWithRange:selectRange];
					newString = [[NSString alloc] initWithFormat:@"(%@)", selectedString];				
				}
				[sourceCodeTextView insertText:newString];
				selectRange = [sourceCodeTextView selectedRealRange];
				[sourceCodeTextView setSelectedRange:NSMakeRange(selectRange.location - 1, 0)];
				[newString release];
			}else
			{
				[sourceCodeTextView xpp_keyDown:event];
			}
		}
			break;
			
		case 39: // " or '
		{
			NSRange selectRange = [sourceCodeTextView selectedRealRange];
			NSString *newString;
			if(selectRange.length == 0)
			{
				if(isShiftPressed)
				{				
					newString = [[NSString alloc] initWithString:@"\"\""];
				}else 
				{
					newString = [[NSString alloc] initWithString:@"''"];
				}
			}
			else {
				if(isShiftPressed)
				{				
					NSString *selectedString = [realString substringWithRange:selectRange];
					newString = [[NSString alloc] initWithFormat:@"@\"%@\"", selectedString];
				}else 
				{
					NSString *selectedString = [realString substringWithRange:selectRange];
					newString = [[NSString alloc] initWithFormat:@"'%@'", selectedString];
				}
			}
			[sourceCodeTextView insertText:newString];
			[newString release];
			selectRange = [sourceCodeTextView selectedRealRange];
			[sourceCodeTextView setSelectedRange:NSMakeRange(selectRange.location - 1, 0)];			
		}
			break;
			
		case 44: // <
		{
			NSRange selectRange = [sourceCodeTextView selectedRealRange];
			NSString *newString;
			if(selectRange.length == 0)
			{
				if(isShiftPressed)
				{				
					newString = [[NSString alloc] initWithString:@"<>"];
				}else 
				{
					[sourceCodeTextView xpp_keyDown:event];
					break;
				}
			}
			else {
				if(isShiftPressed)
				{				
					NSString *selectedString = [realString substringWithRange:selectRange];
					newString = [[NSString alloc] initWithFormat:@"<%@>", selectedString];
				}else 
				{
					[sourceCodeTextView xpp_keyDown:event];
					break;
				}
			}
			[sourceCodeTextView insertText:newString];
			[newString release];
			selectRange = [sourceCodeTextView selectedRealRange];
			[sourceCodeTextView setSelectedRange:NSMakeRange(selectRange.location - 1, 0)];			
		}
			break;
			
		case 8: // back space
		{			
			NSRange selectRange = [sourceCodeTextView selectedRealRange];
			// Fix bug: press back space at the end of file will cause a crash.
			// We need to check that we are not at the end of the file.
			if(selectRange.length == 0 && selectRange.location < [realString length] && [XPPXcodeRegisterMenuItem isRegistered])
			{
				selectRange.length = 1;
				NSString *selectedString = [realString substringWithRange:selectRange];
				selectRange.location = selectRange.location == 0 ? 0 : selectRange.location-1;
				NSString *preString = [realString substringWithRange:selectRange];
				if(([selectedString isEqualToString:@"]"] && [preString isEqualToString:@"["]) ||
				   ([selectedString isEqualToString:@")"] && [preString isEqualToString:@"("]) ||
				   ([selectedString isEqualToString:@"\""] && [preString isEqualToString:@"\""]) ||
				   ([selectedString isEqualToString:@">"] && [preString isEqualToString:@"<"]) ||
				   ([selectedString isEqualToString:@"'"] && [preString isEqualToString:@"'"]) )
				{
					[sourceCodeTextView deleteForward:nil];
				}
			}
			[sourceCodeTextView xpp_keyDown:event];
		}
			break;
			
		case 13: // enter
		{
			NSRange selectRange = [sourceCodeTextView selectedRealRange];
			
			if(selectRange.length == 0 && [XPPXcodeRegisterMenuItem isRegistered])
			{
				if (isShiftPressed) {
					NSRange interfaceRange = [realString rangeOfString:@"@interface" 
															   options:NSBackwardsSearch
																 range:NSMakeRange(0, selectRange.location)];
					
					if (NSNotFound != interfaceRange.location) {
						NSString *aString = [realString substringWithRange:NSMakeRange(selectRange.location, 1)];
						if ([@"\n" isEqualToString:aString] ) {
							NSRange classRange = [realString rangeOfString:@"@end" 
																   options:NSLiteralSearch
																	 range:NSMakeRange(interfaceRange.location, [realString length] - interfaceRange.location)];
							if (NSNotFound != classRange.location) {
								classRange = NSMakeRange(interfaceRange.location, classRange.length + classRange.location - interfaceRange.location);
								NSString *classString = [realString substringWithRange:classRange];
								NSString *className = [[classString subStringSourceBetweenStringStart:@"@interface"
																								  end:@":"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
								
								NSRange rangeOfVaribales = [classString rangeOfStringBetweenStartWith:@"{"
																							  endWith:@"}"];
								
								if (selectRange.location > rangeOfVaribales.location + interfaceRange.location && selectRange.location < rangeOfVaribales.location + interfaceRange.location + rangeOfVaribales.length) {
									NSRange lineRange = [classString rangeOfString:@"\n" 
																		   options:NSBackwardsSearch
																			 range:NSMakeRange(0, selectRange.location - interfaceRange.location)];
									lineRange = NSMakeRange(lineRange.location, selectRange.location - interfaceRange.location-lineRange.location);
									if (NSNotFound != lineRange.location) {
										NSString *selectedLine = [[classString substringWithRange:lineRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
										//do we have a point?
										NSRange pointRange = [selectedLine rangeOfString:@"*"];
										NSString *variableName = nil;
										NSString *variableType = nil;
										BOOL isPoint = NO;
										if (NSNotFound != pointRange.location) {
											//yes we have
											variableType = [selectedLine substringToIndex:pointRange.location +1];
											variableName = [[selectedLine substringFromIndex:pointRange.location +1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
											isPoint = YES;
										}else {
											// no we don't
											NSArray *temp = [selectedLine componentsSeparatedByString:@" "];
											if ([temp count] < 2) {
												temp = [selectedLine componentsSeparatedByString:@"\t"];
											}
											variableName  = [temp lastObject];
											variableType = [temp objectAtIndex:0];
										}
										variableName = [variableName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@";"]];
										if (nil != variableName && [variableName length] > 0) {
											//save data before we start operation
											NSArray *document = [NSApp orderedDocuments];
											for(id obj in document){
												if ([obj isKindOfClass:NSClassFromString(@"XCEditDocument")]) {
													[obj saveDocument:nil];
												}
						
					
											}
											for(id obj in document){
												if([obj isKindOfClass:NSClassFromString(@"PBXProjectDocument")]){
													[obj saveEditedDocumentsIfNeededWithCancelTitle:@"Cancel"];
												}
											}
											[[XPPAutoCompleteHelper sharedXPPAutoCompleteHelper]autoCompletePropertySynthesizeForVariableName:variableName
																																 variableType:variableType
																																  inClassName:className
																														 inSourceCodeTextView:sourceCodeTextView
																																	  isPoint:isPoint];
											[sourceCodeTextView setSelectedRange:selectRange];
											
										}
										
									}
									
								}
							}
						}
						
					}
				}//else {
//					NSString *character = [realString substringToIndex:NSMakeRange(selectRange.location, 1)];
//					//加入这个代码是希望 自动加分号 只在行末起作用 避免发生 我在 多参数函数 断句的时候出现自动上分号的错误
//					
//					if ([character isEqualToString:@"\n"]) {
//						selectRange.length = 1;
//						selectRange.location = selectRange.location == 0 ? 0 : selectRange.location-1;
//						NSString *selectedString = [realString substringWithRange:selectRange];
//						if([selectedString isEqualToString:@"]"]){
//							selectRange.location += 1;
//							selectRange.length = 0;
//							[sourceCodeTextView setSelectedRange:selectRange];
//							[sourceCodeTextView insertText:@";"];
//						}	
//					}
//				
//				}
				
			}
			[sourceCodeTextView xpp_keyDown:event];	
		}
			break;
			
		default:
			[sourceCodeTextView xpp_keyDown:event];
			break;
	}
}

- (void) xpp_keyDown:(NSEvent *) event
{
	// Check our defaults to see if we want to handle keyDown:
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	if ([defaults boolForKey:XPPXCodeEnableDefaultKey]) {
		[XPPXcodeKeyPressed xpp_keyDown:event withObject:self];
	} else {
		[self xpp_keyDown:event];
	}
}

- (void) xpp_selectAll:(id) sender
{
	[self xpp_selectAll:sender];
	// Check our defaults to see if we want to indent document
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	if ([defaults boolForKey:XPPXcodeSelectAllToReindent] && [XPPXcodeRegisterMenuItem isRegistered]) {
		[(XCSourceCodeTextView*)self indentSelection:self];
	} 
	
}

@end
